#region Imported namespaces
using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;

using BO = PHSRAG.Insight21.BusinessObjects;
using Utility = PHSRAG.Utility;
using ICachePolicy = PHSRAG.Insight21.Policy.ICachePolicy;
#endregion

namespace PHSRAG.Insight21.FormsManagement
{
	#region Class Questionnaire
	/// <summary>
	/// Questionnaire class abstracts the notion of a Protocol Questionnaire. A questionnaire is a collection of questions.
	/// A Questionnaire object holds the details of a Protocol Questionnaire like Name, Description etc and a collection of all its versions.
	/// All the versions are stored in a hashtable with the Id of the version as the key and the object as the value.
	/// </summary>
	public class Questionnaire : BO.SmartBusinessObject, ICloneable
	{

		#region Constants
		private const string ColumnNameId = "Id";
		private const string ColumnNameName = "Name";
		private const string ColumnNameType = "Type";
		private const string ColumnNameTypeMeaning = "TypeMeaning";
		private const string ColumnNameCategory = "Category";
		private const string ColumnNameCategoryMeaning = "CategoryMeaning";
		private const string ColumnNameCategorySequenceNo = "CategorySequenceNo";
		private const string ColumnNameDescription = "Description";
		private const string ColumnNameAllowMultiple = "AllowMultiple";
		private const string ColumnNameActive = "Active";
		private const string ColumnNameModuleType = "ModuleType";
		private const string QuestionnaireKey = "QuestionnaireKey_ED37D620-B0A8-4574-BF21-C963F8BFE0A5";
		#endregion

		#region Instance Variables (private)
		private string name;
		private string type;
		private string typeMeaning;
		private string category;
		private string categoryMeaning;
		private int categorySequenceNo;
		private string description;
		private bool allowMultiple;
		private bool active;
		private int moduleType;
		private ArrayList versions;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new Questionnaire object based on the Cache Policy passed in as a parameter. 
		/// </summary>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		public Questionnaire(ICachePolicy cachePolicy) : base(cachePolicy)
		{
		
		}


		/// <summary>
		/// Constructs a new Questionnaire object based on the Cache Policy passed in as a parameter. 
		/// All the instance variables will be set to the corresponding values specified in the Data Row
		/// </summary>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		/// <param name="dataRow">DataRow holding the values to be assigned to instance variables</param>
		public Questionnaire(ICachePolicy cachePolicy, DataRow dataRow ) : base(cachePolicy)
		{
			PopulateDataMembers(dataRow);
		}

		#endregion

        #region ICloneable
        object ICloneable.Clone()
        {
            return Clone( );
        }
        #endregion

        public virtual Questionnaire Clone()
        {
            Questionnaire clone;
            try
            {
                clone = (Questionnaire)MemberwiseClone();
                clone.Id = 0;
                clone.Dirty = true;

                clone.versions = new ArrayList();
                foreach (QuestionnaireVersion version in Versions)
                {
                    clone.versions.Add(version.Clone( ));
                }
            }
            catch (Exception ex)
            {
                Utility.ExceptionReport.WriteToEventLog(
                    "Questionnaire.Clone", ex, true, Utility.ExceptionReport.ReportType.Error);
                throw;
            }

            return clone;
        }

        #region Properties
        /// <summary>
		/// Gets or sets the name of the Questionnaire
		/// </summary>
		public string Name
		{
			get { return name; }
			set	{ SetDirty(ref name,value);	}
		}


		/// <summary>
		/// Gets or sets the detailed description of the Questionnaire
		/// </summary>
		public string Description
		{
			get { return description; }
			set	{ SetDirty(ref description,value); }
		}

		
		/// <summary>
		/// Gets or sets the type of the Questionnaire 
		/// </summary>
		public string Type
		{
			get { return type; }
			set	{ SetDirty(ref type,value);	}
		}
		
		
		/// <summary>
		/// Gets or sets the description of the type of the Questionnaire
		/// </summary>
		public string TypeMeaning
		{
			get { return typeMeaning; }
			set { typeMeaning = value; }
		}


		/// <summary>
		/// Gets or sets the category of the Questionnaire
		/// </summary>
		public string Category
		{
			get { return category; }
			set	{ SetDirty(ref category, value); }
		}
	
		
		/// <summary>
		/// Gets or sets the description of the category of the Questionnaire
		/// </summary>
		public string CategoryMeaning
		{
			get { return categoryMeaning; }
			set { categoryMeaning = value; }
		}


		/// <summary>
		/// Gets or sets the sequence number for the questionnaire within its category
		/// </summary>
		public int CategorySequenceNo
		{
			get { return categorySequenceNo; }
			set { SetDirty(ref categorySequenceNo, value);} 
		}


		/// <summary>
		/// Determines whether or not this Questionnaire can be 
		/// added multiple times (ex. Drugs, Devices, Children, etc.)
		/// </summary>
		public bool AllowMultiple
		{
			get { return allowMultiple; }
			set { SetDirty ( ref allowMultiple, value ); }
		}


		/// <summary>
		/// Gets or sets the type of the module to which the questionnaire is associated to. ex: Humans, COI etc
		/// </summary>
		public int ModuleType
		{
			get { return moduleType; }
			set { SetDirty ( ref moduleType, value ); }
		}


		/// <summary>
		/// Gets or sets whether or not this Questionnaire has got at least one version which is active
		/// </summary>
		public bool Active
		{
			get { return active; }
			set { active = value; }
		}


		/// <summary>
		/// Gets or sets the versions collection of the questionnaire
		/// </summary>
		public virtual ArrayList Versions
		{
			get 
			{
				if (versions == null)
					versions = QuestionnaireVersion.GetQuestionnaireVersions(CachePolicy, Id);
				return versions; 
			}
			set { versions = value;	}
		}

		#endregion

		#region SmartBusinessObject methods
		/// <summary>
		/// Cache Key uniquely identifies this object within the cache.
		/// </summary>
		/// <returns>the unique string for this object.</returns>
		public override string CacheKey()
		{
			return QuestionnaireKey;
		}

		
		/// <summary>
		/// Cache this instance using the prevalent cache policy.
		/// </summary>
		public override void Cache()
		{
			CachePolicy.Cache(QuestionnaireKey, this);
		}

		
		/// <summary>
		/// Remove this instance from the cache using the prevalent cache policy.
		/// </summary>
		public override void UnCache()
		{
			CachePolicy.UnCache(QuestionnaireKey);
		}

		
		/// <summary>
		/// Saves the state of the Questionnaire object to the database. 
		/// The stored procedure inserts a new record if the id of the object is 0, 
		/// otherwise the procedure updates the record with the matching questionnaire id
		/// </summary>
		/// <param name="sqlTransaction">The database context in which the underlying sql statement needs to be executed</param>
		protected override void SaveObject(SqlTransaction sqlTransaction)
		{
			try
			{
                bool newQuestionnaire = (Id == 0);
				Id = Convert.ToInt32(Utility.DataAccess.ExecuteScalar(sqlTransaction.Connection,sqlTransaction, "SaveQuestionnaire", 
					Utility.DataAccess.BuildSqlParameterArray(
					"@id", SqlDbType.Int, Id
					,"@name", SqlDbType.VarChar, name
					,"@type", SqlDbType.VarChar, type
					,"@category", SqlDbType.VarChar, category
					,"@categorySequenceNo", SqlDbType.VarChar, categorySequenceNo
					,"@description", SqlDbType.VarChar, description
					,"@allowMultiple", SqlDbType.Bit, allowMultiple
					,"@moduleType", SqlDbType.Int, (moduleType==0) ? (object)DBNull.Value : moduleType)));

                if (newQuestionnaire && (versions == null || versions.Count == 0))
                {
                    versions = new ArrayList();
                    QuestionnaireVersion newQV = new QuestionnaireVersion( CachePolicy );
                    newQV.Description = "Initial Version";
                    newQV.IsActive = false;
                    newQV.IsDeployed = false;
                    newQV.Version = 1;
                    versions.Add(newQV);
                }

			}
			catch(Exception ex)
			{
				throw new Exception("Questionnaire.SaveObject: Failed to save the Questionnaire object to the database", ex);
			}	
			
			if (versions != null)
			{
				foreach(QuestionnaireVersion version in versions)
				{
					version.QuestionnaireID = Id;
					version.Save(sqlTransaction);
				}
			}
		}
		

		/// <summary>
		/// Deletes the record represented by the object from the database
		/// <para>This method is not yet implemented</para> 
		/// </summary>
		/// <param name="sqlTransaction">The database context in which the underlying sql statement needs to be executed</param>
		protected override void DeleteObject(SqlTransaction sqlTransaction)
		{
			throw new Exception("Not Implemented");
		}


		/// <summary>
		/// Loads the Questionnaire object based on the id passed in as parameter
		/// </summary>
		/// <param name="connectionString">Information to connect to the database</param>
		/// <param name="args">Questionnaire Id of the object to be loaded</param>
		public override void Load(string connectionString, params object[] args)
		{
			if ( args.Length != 1 )
				throw new Exception("Questionnaire.Load expects ID only.");

			try
			{
				DataSet newDataSet = Utility.DataAccess.GetDataSet( connectionString, "GetQuestionnaire", 
					Utility.DataAccess.BuildSqlParameterArray("@id", SqlDbType.Int, (int)args[0]));
				if (newDataSet.Tables.Count > 0)
				{
					DataRowCollection rows = newDataSet.Tables[0].Rows;
					if (rows.Count == 1)
						PopulateDataMembers( rows[0] );
					else
						throw new Exception("No questionnaire with the specified id exists in the database");
				}
				else
				{
					throw new Exception("GetQuestionnaire stored procedure returned incorrect results");
				}
			}
			catch(Exception ex)
			{
				Utility.ExceptionReport.WriteToEventLog ("Failed to load Questionnaire object", ex, true, Utility.ExceptionReport.ReportType.Error);
				throw new Exception("Failed to load Questionnaire object", ex);
			}
		}

		
		/// <summary>
		/// Determines if the object is dirty based on whether or not atleast one of its properties have changed or any of its contained objects have changed
		/// </summary>
		/// <returns>true - if the object is dirty or any of its contained objects are dirty, false - if not.</returns>
		public override bool IsDirty()
		{
			if (Dirty)
				return true;

			if (versions != null)
			{
				foreach(QuestionnaireVersion version in versions)
				{
					if (version.IsDirty())
						return true;
				}
			}

			return false;
		}
		 
		#endregion

		#region Static methods
		/// <summary>
		/// Gets all the existing questionnaire objects from the database
		/// </summary>
		/// <param name="moduleType">BitValue of the module the user is viewing</param>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		/// <returns>Hashtable holding the questionnaires with id as the key and the questionnaire object as the value</returns>
		public static Hashtable GetQuestionnaires(int moduleType, ICachePolicy cachePolicy)
		{
			Hashtable questionnaireList = new Hashtable();
			try
			{
				DataSet ds = Utility.DataAccess.GetDataSet(Utility.DataAccess.GetConnectionString(), "GetQuestionnaires",
					Utility.DataAccess.BuildSqlParameterArray(
					"@moduleType", SqlDbType.Int, moduleType));
				if (ds.Tables.Count > 0)
				{
					foreach( DataRow row in ds.Tables[0].Rows )
					{
						Questionnaire questionnaire = new Questionnaire(cachePolicy, row);
						questionnaireList.Add (questionnaire.Id, questionnaire);
					}
				}
				else
				{
					throw new Exception("GetQuestionnaires stored procedure returned incorrect results");
				}
			}
			catch(Exception ex)
			{
				Utility.ExceptionReport.WriteToEventLog ("Questionnaire.GetQuestionnaireVersions: Failed to load Questionnaires", ex, true, Utility.ExceptionReport.ReportType.Error);
				throw new Exception("Questionnaire.GetQuestionnaireVersions: Failed to load Questionnaires", ex);
			}
			return questionnaireList;
		}

		#endregion

		#region Public methods
		/// <summary>
		/// Creates a new QuestionnaireVersion object, adds it to the versions arraylist and returns the new object. 
		/// </summary>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		/// <returns>A new QuestionnaireVersion object</returns>
		public QuestionnaireVersion CreateVersion(ICachePolicy cachePolicy)
		{
			QuestionnaireVersion newVersion = new QuestionnaireVersion(cachePolicy);
			newVersion.QuestionnaireID = Id;
			newVersion.Version = GetMaxVersionNumber() + 1;
            Versions.Add(newVersion);
            Dirty = true;
			return newVersion;
		}

        /// <summary>
        /// Get a particular QuestionnaireVersion from 
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public QuestionnaireVersion GetQuestionnaireVersionById(int Id)
        {
            foreach (QuestionnaireVersion version in Versions)
            {
                if (version.Id == Id)
                    return version;
            }
            Utility.ExceptionReport.WriteToEventLog("Questionnaire.GetQuestionnaireVersionById", "Failed to find QuestionnaireVersion", Utility.ExceptionReport.ReportType.Error);
            throw new Exception("Questionnaire.GetQuestionnaireVersionById: Failed to find QuestionnaireVersion");
        }

		/// <summary>
		/// Gets the active version for the questionnaire with the specified name.
		/// </summary>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		/// <param name="questionnaireName">Name of the questionnaire</param>
		/// <param name="questionnaireType">Type of the questionnaire</param>
		/// <returns>The active questionnaire version, if exists else returns null</returns>
		public static QuestionnaireVersion GetActiveVersion ( ICachePolicy cachePolicy, 
			string questionnaireName, 
			string questionnaireType )
		{
			QuestionnaireVersion activeVersion = null;

			try
			{
				DataSet ds = Utility.DataAccess.GetDataSet(
					Utility.DataAccess.GetConnectionString(), "GetActiveQuestionnaireVersion",
					Utility.DataAccess.BuildSqlParameterArray(
					"@name", SqlDbType.VarChar, questionnaireName,
					"@questionnaireType", SqlDbType.VarChar, questionnaireType ));

				if (ds.Tables.Count > 0)
				{
					DataRowCollection rows = ds.Tables[0].Rows;
			
					if (rows.Count > 0)
						activeVersion = new QuestionnaireVersion(cachePolicy, rows[0]);
				}
				else
				{
					throw new Exception("GetActiveQuestionnaireVersion stored procedure returned incorrect results.");
				}
			}
			catch(Exception ex)
			{
				throw new Exception("Unable to load active Questionnaire.", ex);
			}
			return activeVersion;

		}

		/// <summary>
		/// Different way to get the active version of a questionnaire using the type and category. This method
		/// is primarily used when looking up the appropriate New Protocol application (Standard or Emergency use)
		/// </summary>
		/// <param name="cachePolicy">prevalent cache policy</param>
		/// <param name="questionnaireCategory">RefCode for teh Catgegory</param>
		/// <param name="questionnaireType">RefCode for teh QuestionnaireType</param>
		/// <returns></returns>
		public static QuestionnaireVersion GetActiveVersionByCategoryAndType ( ICachePolicy cachePolicy, 
			string questionnaireCategory, 
			string questionnaireType )
		{
			QuestionnaireVersion activeVersion = null;

			try
			{
				DataSet ds = Utility.DataAccess.GetDataSet(
					Utility.DataAccess.GetConnectionString(), "GetActiveQuestionnaireVersionByType",
					Utility.DataAccess.BuildSqlParameterArray(
					"@questionnaireCategory", SqlDbType.VarChar, questionnaireCategory,
					"@questionnaireType", SqlDbType.VarChar, questionnaireType ));

				if (ds.Tables.Count > 0)
				{
					DataRowCollection rows = ds.Tables[0].Rows;
			
					if (rows.Count > 0)
						activeVersion = new QuestionnaireVersion(cachePolicy, rows[0]);
				}
				else
				{
					throw new Exception("GetActiveVersionByCategoryAndType stored procedure returned incorrect results.");
				}
			}
			catch(Exception ex)
			{
				throw new Exception("Unable to load active Questionnaire.", ex);
			}
			return activeVersion;

		}

		
		/// <summary>
		/// Fetches the active questionnaire version for the version in use.
		/// 
		/// This method will be useful when a questionnaire answer is cloned and the new questionnaire answer 
		/// should be tied to the active version at the time of cloning.
		/// </summary>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		/// <param name="versionIdInUse">Id of the Questionnaire Version that is in use. Could be an inactive version.</param>
		/// <returns>Active QuestionnaireVersion</returns>
		public static QuestionnaireVersion GetActiveVersion (ICachePolicy cachePolicy, int versionIdInUse)
		{
			QuestionnaireVersion activeVersion = null;

			try
			{
				DataSet ds = Utility.DataAccess.GetDataSet(
					Utility.DataAccess.GetConnectionString(), "GetActiveQuestionnaireVersionByVersionIdInUse",
					Utility.DataAccess.BuildSqlParameterArray(
					"@versionIdInUse", SqlDbType.Int, versionIdInUse));

				if (ds.Tables.Count > 0)
				{
					DataRowCollection rows = ds.Tables[0].Rows;
			
					if (rows.Count > 0)
						activeVersion = new QuestionnaireVersion(cachePolicy, rows[0]);
					else
					{
						throw new Exception("No version is active.");
					}
				}
				else
				{
					throw new Exception("GetActiveVersion stored procedure returned incorrect results.");
				}
			}
			catch(Exception ex)
			{
				throw new Exception("Unable to load active Questionnaire.", ex);
			}
			return activeVersion;
		}

        public static DataSet GetFormTypes(int protocolversionid)
        {
           
            DataSet ds;
            try
            {
                 ds = Utility.DataAccess.GetDataSet(
                    Utility.DataAccess.GetConnectionString(), "GetFormTypes",
                    Utility.DataAccess.BuildSqlParameterArray(
                    "@protocolprocessid", SqlDbType.Int, protocolversionid));

                return ds;
                
            }
            catch (Exception ex)
            {
                throw new Exception("Unable to load active Questionnaire.", ex);
            }            
        }
		#endregion

		#region Private methods
		/// <summary>
		/// Gets the maximum version number for the questionnaire
		/// </summary>
		/// <returns>Highest version number</returns>
		private int GetMaxVersionNumber()
		{
			int maxVersionNumber = 0;
			foreach(QuestionnaireVersion questionnaireVersion in Versions)
			{
				if (maxVersionNumber < questionnaireVersion.Version)
					maxVersionNumber = questionnaireVersion.Version;
			}
			return maxVersionNumber;
		}

		
		/// <summary>
		/// Populates instance fields with corresponding values in the datarow
		/// </summary>
		/// <param name="dataRow">DataRow holding the values to be assigned to instance variables</param>
		private void PopulateDataMembers(DataRow dataRow)
		{
			Id = (int)dataRow[ColumnNameId];
			name = (string)dataRow[ColumnNameName];
			type = (string)dataRow[ColumnNameType];
			typeMeaning = (string)dataRow[ColumnNameTypeMeaning];
			category = (string)dataRow[ColumnNameCategory];
			categoryMeaning = (string)dataRow[ColumnNameCategoryMeaning];
			categorySequenceNo = (int)dataRow[ColumnNameCategorySequenceNo];
			object o = dataRow[ColumnNameDescription];
			description	= (o == DBNull.Value) ? string.Empty : (string)o;
			allowMultiple = (bool)dataRow[ColumnNameAllowMultiple];

			if (dataRow.Table.Columns.Contains(ColumnNameActive))
				active = (bool)dataRow[ColumnNameActive];

			if (dataRow.Table.Columns.Contains(ColumnNameModuleType))
				moduleType = (int)dataRow[ColumnNameModuleType];
		}
		#endregion
	
	}
	#endregion

}
